#ifndef RTPPARSE_UDP_SOCKET_HANDLE_H
#define RTPPARSE_UDP_SOCKET_HANDLE_H

#include "uv.h"
#include <string>
#include <functional>

class UdpSocketHandle {

protected:
    using onSendCallback = const std::function<void(bool sent)>;
public:

    struct UvSendData {
        explicit UvSendData(size_t storeSize): store(new uint8_t[storeSize]){}

        UvSendData(const UvSendData&) = delete;

        ~UvSendData() {
            delete[] store;
        }

        uv_udp_send_t req {};
        uint8_t* store{ nullptr };
        UdpSocketHandle::onSendCallback *cb { nullptr };
    };

public:
    explicit UdpSocketHandle(uv_udp_t *handle);

    UdpSocketHandle& operator=(const UdpSocketHandle&) = delete;

    UdpSocketHandle(const UdpSocketHandle&) = delete;

    virtual ~UdpSocketHandle();

public:
    bool IsClosed() const {
        return this->closed;
    }

    void Send(const uint8_t* data, size_t len,
              const struct sockaddr* addr, UdpSocketHandle::onSendCallback* cb);

    const struct sockaddr* GetLocalAddress() const {
        return reinterpret_cast<const struct sockaddr*>(&this->localAddr);
    }

    int GetLocalFamily() const {
        return reinterpret_cast<const struct sockaddr*>(&this->localAddr)->sa_family;
    }

    const std::string& GetLocalIp() const {
        return this->localIp;
    }

    uint16_t GetLocalPort() const {
        return this->localPort;
    }

    size_t GetRecvBytes() const {
        return this->recvBytes;
    }

    size_t GetSentBytes() const {
        return this->sentBytes;
    }

    uint32_t GetSendBufferSize() const;
    void SetSendBufferSize(uint32_t size);
    uint32_t GetRecvBufferSize() const;
    void SetRecvBufferSize(uint32_t size);
private:
    void InternalClose();
    bool SetLocalAddress();

public:
    void OnUvRecvAlloc(size_t suggestedSize, uv_buf_t* buf);
    void OnUvRecv(ssize_t nread, const uv_buf_t* buf,
                  const struct sockaddr* addr, unsigned flags);
    void OnUvSend(int status, UdpSocketHandle::onSendCallback* cb);

protected:
    virtual void UserOnUdpDatagramReceived(const uint8_t* data, size_t len,
                                           const struct sockaddr* addr) = 0;
protected:
    struct sockaddr_storage localAddr {};
    std::string localIp;
    uint16_t localPort { 0u };
private:
    uv_udp_t *uvHandle { nullptr};
    bool closed { false };
    size_t recvBytes { 0u };
    size_t sentBytes { 0u };
};

#endif